#include "stdafx.h"
#include "CppUnitTest.h"

#include <HSModel/SharedConstants.h>
#include <HSModel/HeightMap.h>
#include <HSModel/Ground.h>
#include <HSModel/Landscape.h>
#include <HSModel/GeometryUtils.h>


#include <math.h> 
using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace HSModelTests
  {
  TEST_CLASS(GroundTest)
    {
    public:

      TEST_METHOD(GroundCreationTest)
        {
        HeightMap height_map = HeightMap(GroundConstants::CELL_COUNT + 1, GroundConstants::CELL_COUNT + 1);
        GeneratedHeightMapWithDiamondSquare(height_map, GroundConstants::PLAINS_PERCENTAGE, GroundConstants::MAX_HEIGHT);
        Ground G1(height_map, GroundConstants::CELL_SIZE, GroundConstants::WATER_LEVEL);
        auto& model = G1.GetLandscape().GetModel();
        bool flag = true;
        for(auto iter = model.Points().begin(); iter != model.Points().end(); iter++)
          {
          if((*iter).Z() > GroundConstants::MAX_HILL_HEIGHT || (*iter).Z() < GroundConstants::MAX_HOLE_DEPTH)
            {
            flag = false;
            break;
            }
          }
        Assert::IsTrue(flag);

        }
			
			TEST_METHOD(SetGroundTypeOutOfRange)
        {
        HeightMap height_map = HeightMap(GroundConstants::CELL_COUNT + 1, GroundConstants::CELL_COUNT + 1);
        GeneratedHeightMapWithDiamondSquare(height_map, GroundConstants::PLAINS_PERCENTAGE, GroundConstants::MAX_HEIGHT);
        Ground G1(height_map, GroundConstants::CELL_SIZE, GroundConstants::WATER_LEVEL);
				bool is_processed_without_exeption;
				try
					{
					G1.SetGroundType(2, 2, GT_WALKWAY);
					is_processed_without_exeption = true;
					}
				catch(...)
					{
					is_processed_without_exeption = false;
					}
				Assert::IsTrue(is_processed_without_exeption);
				}
      TEST_METHOD(CreateHighland_InCenter)
        {
        HeightMap height_map = HeightMap(GroundConstants::CELL_COUNT + 1, GroundConstants::CELL_COUNT + 1);
        GeneratedHeightMapWithDiamondSquare(height_map, GroundConstants::PLAINS_PERCENTAGE, GroundConstants::MAX_HEIGHT);
        Ground G1(height_map, GroundConstants::CELL_SIZE, GroundConstants::WATER_LEVEL);
        //test point 1
        auto& model = G1.GetLandscape().GetModel();
        auto iter = model.Points().begin();
        size_t point_number = 4;
        for(size_t i = 0; i < point_number; ++i)
          {
          iter++;
          }
        double expected=(*iter).Z() + GroundConstants::HMAP_CHANGE * GroundConstants::CELL_SIZE;
        double x =  static_cast<size_t>((*iter).X());
        double y =  static_cast<size_t>((*iter).Y());
				G1.CreateHighland(TVector2(x, y), GroundConstants::HMAP_CHANGE);

        auto iter2 = model.Points().begin();
        for(size_t i = 0; i < point_number; ++i)
          {
          iter2++;
          }
        Assert::AreEqual(expected, (*iter2).Z(), 1e-7);
        }

      TEST_METHOD(CreateHighland_OnEdge)
        {
        //test point 2
        HeightMap height_map = HeightMap(GroundConstants::CELL_COUNT + 1, GroundConstants::CELL_COUNT + 1);
        GeneratedHeightMapWithDiamondSquare(height_map, GroundConstants::PLAINS_PERCENTAGE, GroundConstants::MAX_HEIGHT);
        Ground G1(height_map, GroundConstants::CELL_SIZE, GroundConstants::WATER_LEVEL);
        auto& model = G1.GetLandscape().GetModel();
        auto iter3 = model.Points().begin();
        size_t point_number = 11; 
        for(size_t i = 0; i < point_number; ++i)
          {
          iter3++;
          }
        double expected=(*iter3).Z() + GroundConstants::HMAP_CHANGE * GroundConstants::CELL_SIZE;
        double x =  static_cast<size_t>((*iter3).X());
        double y =  static_cast<size_t>((*iter3).Y());
        G1.CreateHighland(TVector2(x, y), GroundConstants::HMAP_CHANGE);

        auto iter4 = model.Points().begin();
        for(size_t i = 0; i < point_number; ++i)
          {
          iter4++;
          }
        Assert::AreEqual(expected, (*iter4).Z(), 1e-7);
        } 

      TEST_METHOD(CreateHole_OnEdge)
        {
        HeightMap height_map = HeightMap(GroundConstants::CELL_COUNT + 1, GroundConstants::CELL_COUNT + 1);
        GeneratedHeightMapWithDiamondSquare(height_map, GroundConstants::PLAINS_PERCENTAGE, GroundConstants::MAX_HEIGHT);
        Ground G1(height_map, GroundConstants::CELL_SIZE, GroundConstants::WATER_LEVEL);
        //test point 1
        auto& model = G1.GetLandscape().GetModel();
        auto iter = model.Points().begin();
        size_t point_number = 12;
        for(size_t i = 0; i < point_number; ++i)
          {
          iter++;
          } 
        double expected = (*iter).Z() - GroundConstants::HMAP_CHANGE * GroundConstants::CELL_SIZE;
        double x = static_cast<size_t>((*iter).X());
        double y = static_cast<size_t>((*iter).Y());
        G1.CreateHole(TVector2(x, y), GroundConstants::HMAP_CHANGE);

        auto iter2 = model.Points().begin(); 
        for(size_t i = 0; i < point_number; ++i)
          {
          iter2++;
          }
        Assert::AreEqual(expected, (*iter2).Z());
        }
      //test point 2
      TEST_METHOD(CreateHole_InCentre)
        {
        HeightMap height_map = HeightMap(GroundConstants::CELL_COUNT + 1, GroundConstants::CELL_COUNT + 1);
        GeneratedHeightMapWithDiamondSquare(height_map, GroundConstants::PLAINS_PERCENTAGE, GroundConstants::MAX_HEIGHT);
        Ground G1(height_map, GroundConstants::CELL_SIZE, GroundConstants::WATER_LEVEL);
        auto& model = G1.GetLandscape().GetModel();
        auto iter3 = model.Points().begin();
        size_t point_number = 10;
        for(size_t i = 0; i < point_number; ++i)
          {
          iter3++;
          }
        double expected = (*iter3).Z() - GroundConstants::HMAP_CHANGE * GroundConstants::CELL_SIZE;
        double x = static_cast<size_t>((*iter3).X());
        double y = static_cast<size_t>((*iter3).Y());
        G1.CreateHole(TVector2(x, y), GroundConstants::HMAP_CHANGE);

        auto iter4 = model.Points().begin();
        for(size_t i = 0; i < point_number; ++i)
          {
          iter4++;
          }
        Assert::AreEqual(expected, (*iter4).Z());
        } 

      TEST_METHOD(HeightInterpolationVerticesTest)
        {
        HeightMap hm(2,2);
        hm.Set(0, 0, 0);
        hm.Set(0, 1, 0);
        hm.Set(1, 0, 1);
        hm.Set(1, 1, 2);

        Ground ground = Ground(hm, GroundConstants::CELL_SIZE, GroundConstants::WATER_LEVEL);

        double h00_expected = 0.0;
        double h01_expected = 0.0;
        double h10_expected = 1.0;
        double h11_expected = 2.0;

        double h00_actual = ground.GetHeightForXY(TVector2(0, 0));
        double h01_actual = ground.GetHeightForXY(TVector2(0, GroundConstants::CELL_SIZE));
        double h10_actual = ground.GetHeightForXY(TVector2(GroundConstants::CELL_SIZE, 0));
        double h11_actual = ground.GetHeightForXY(TVector2(GroundConstants::CELL_SIZE, GroundConstants::CELL_SIZE));

        Assert::AreEqual(h00_expected, h00_actual);
        Assert::AreEqual(h01_expected, h01_actual);
        Assert::AreEqual(h10_expected, h10_actual);
        Assert::AreEqual(h11_expected, h11_actual);
        }

      //
      //This test doesn't pass because of inconsistency in coordinate systems of OGRE and height map
      //
      
      TEST_METHOD(HeightInterpolationMiddleTest)
        {
        HeightMap hm(2,2);
        hm.Set(0, 0, 0);
        hm.Set(0, 1, 0);
        hm.Set(1, 0, 1);
        hm.Set(1, 1, 2);

        Ground ground = Ground(hm, GroundConstants::CELL_SIZE, GroundConstants::WATER_LEVEL);

        double h_middle_expected = 0.5;
        double h_top_expected = 0.25;
        double h_bottom_expected = 0.75;

        double h_middle_actual = ground.GetHeightForXY(TVector2((double)GroundConstants::CELL_SIZE/2, (double)GroundConstants::CELL_SIZE/2));
        double h_top_actual = ground.GetHeightForXY(TVector2((double)GroundConstants::CELL_SIZE / 4, (double)GroundConstants::CELL_SIZE * 3 / 4));
        double h_bottom_actual = ground.GetHeightForXY(TVector2((double)GroundConstants::CELL_SIZE * 3 / 4, (double)GroundConstants::CELL_SIZE / 4));
                                         
        Assert::AreEqual(h_middle_expected, h_middle_actual);
        Assert::AreEqual(h_top_expected, h_top_actual);
        Assert::AreEqual(h_bottom_expected, h_bottom_actual);
        }

      TEST_METHOD(HeightInterpolationInsideTest)
        {
        HeightMap hm(2,2);
        hm.Set(0, 0, 0);
        hm.Set(0, 1, 0);
        hm.Set(1, 0, 1);
        hm.Set(1, 1, 2);

        Ground ground = Ground(hm, GroundConstants::CELL_SIZE, GroundConstants::WATER_LEVEL);

        double h_inside_top_expected = 0.25;
        double h_inside_bottom_expected = 1.25;

        double h_inside_top_actual = ground.GetHeightForXY(TVector2((double)GroundConstants::CELL_SIZE / 4, (double)GroundConstants::CELL_SIZE / 4));
        double h_inside_bottom_actual = ground.GetHeightForXY(TVector2((double)GroundConstants::CELL_SIZE * 3 / 4, (double)GroundConstants::CELL_SIZE * 3 / 4));
                                         
        Assert::AreEqual(h_inside_top_expected, h_inside_top_actual);
        Assert::AreEqual(h_inside_bottom_expected, h_inside_bottom_actual);
        }

    };
  }